/**
 * @author: yyq
 * @date: 2025年3月18日 11:17:57
 * @description: 
 */
package com.sheepone.monitor.audit.aspect;

import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

import javax.annotation.Resource;

import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;

import com.sheepone.monitor.audit.AuditMonitorBootClient;
import com.sheepone.monitor.audit.SceneProcessor;
import com.sheepone.monitor.audit.anno.AuditCommit;
import com.sheepone.monitor.audit.autoconfigure.AuditClientBootConfig;
import com.sheepone.monitor.audit.core.entities.AuditLogMessage;
import com.sheepone.webdev.log.WebDevLogger;

/**
 * @author: yyq
 * @date: 2025年3月18日 11:17:57
 * @description:
 */
@Aspect
public class AuditCommitAspect {
    private AuditClientBootConfig config;
    private static final String BEFORE_SUFFIX = "_BEFORE";
    private static final String AFTER_SUFFIX = "_AFTER";
    private Map<String, SceneProcessor> sceneProcessors;

    private static WebDevLogger logger = WebDevLogger.getLogger(AuditCommitAspect.class);

    @Resource
    private AuditMonitorBootClient monitorClient;

    public AuditCommitAspect(AuditClientBootConfig config) {
        this.config = config;
    }

    @Around("@annotation(auditCommit)")
    public Object logMethodExecution(ProceedingJoinPoint joinPoint, AuditCommit auditCommit) throws Throwable {
        boolean auto = auditCommit.auto();
        String scene = StringUtils.isNotBlank(auditCommit.scene()) ? auditCommit.scene() : config.getDefaultScene();
        AuditLogMessage message = new AuditLogMessage();
        message.setAction(auditCommit.action());
        message.setModule(auditCommit.module());
        message.setRequestTime(LocalDateTime.now());
        message.setService(config.getService());
        sceneBefore(scene, message);
        long startTime = System.nanoTime();
        boolean success = true;
        try {
            return joinPoint.proceed();
        } catch (Exception e) {
            success = false;
            throw e;
        } finally {
            if (auto) {
                sceneAfter(scene, message);
                message.setDuration((System.nanoTime() - startTime) / 1_000.0);
                if (message.getStatus() == null) {
                    message.setStatus(success ? config.getStatusOnSuccess() : config.getStatusOnException());
                }
                monitorClient.send(message);
            }
        }
    }

    private void sceneBefore(String scene, AuditLogMessage message) {
        if (!config.getSceneEnable() || StringUtils.isEmpty(scene) || sceneProcessors == null) {
            return;
        }
        SceneProcessor processor = sceneProcessors.get(scene + BEFORE_SUFFIX);
        if (processor != null) {
            processor.process(message);
        }
    }

    private void sceneAfter(String scene, AuditLogMessage message) {
        if (!config.getSceneEnable() || StringUtils.isEmpty(scene) || sceneProcessors == null) {
            return;
        }
        SceneProcessor processor = sceneProcessors.get(scene + AFTER_SUFFIX);
        if (processor != null) {
            processor.process(message);
        }
    }

    public void registerBeforeSceneProcessor(String scene, Object obj, Method method) {
        if (sceneProcessors == null) {
            sceneProcessors = new HashMap<>();
            sceneProcessors.put(scene + BEFORE_SUFFIX, new SceneProcessor(obj, method));
            return;
        }
        SceneProcessor processor = sceneProcessors.get(scene);
        if (processor == null) {
            sceneProcessors.put(scene + BEFORE_SUFFIX, new SceneProcessor(obj, method));
            return;
        }
        logger.warn("Conflicting BeforeSceneProcessors: {} and {}, for scene: {}, {}'s registration failed.",
                processor.getName(), method.getName(), scene, method.getName());

    }

    public void registerAfterSceneProcessor(String scene, Object obj, Method method) {
        if (sceneProcessors == null) {
            sceneProcessors = new HashMap<>();
            sceneProcessors.put(scene + AFTER_SUFFIX, new SceneProcessor(obj, method));
            return;
        }
        SceneProcessor processor = sceneProcessors.get(scene);
        if (processor == null) {
            sceneProcessors.put(scene + AFTER_SUFFIX, new SceneProcessor(obj, method));
            return;
        }
        logger.warn("Conflicting AfterSceneProcessors: {} and {}, for scene: {}, {}'s registration failed.",
                processor.getName(), method.getName(), scene, method.getName());
    }
}
